package com.example.batchdemo.config;

import com.example.batchdemo.model.BulkAdjustment;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;


/**
 * 该类是与上传文件批量更新数据相关的工具类。
 *
 * @author Vince Qian
 * @since 22 Jan 2019
 * @version 1.0
 *
 */
@Slf4j
@Component
public class BulkAdjustmentFileHandler {

	public static final String SHEET_NAME_DATA = "Data";
	public static final String SHEET_NAME_HELP = "Help";
	public static final String EXCEL_EXET_XLSX = "xlsx";
	
	private static final String COLUMN_NAME_ROW_NUM = "rowNum";

	@Value("${bulk.adjustment.file.outputPath}")
	private String outputPath;

	public String getOutputPath() {
		if (outputPath.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
			try {
				outputPath = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX).getPath()
						+ outputPath.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length());
			} catch (FileNotFoundException e) {
			}
		}
		return outputPath;
	}

	public BulkAdjustment uploadFile(byte[] fileBytes, String fileName) throws Exception {
		String fullFilePath = "";

		String fileNameWithoutExet = fileName.substring(0, fileName.lastIndexOf("."));
		String fileExet = fileName.substring(fileName.lastIndexOf(".") + 1);

		if (!EXCEL_EXET_XLSX.equalsIgnoreCase(fileExet)) {
			log.debug("BulkAdjustmentFileHandler.uploadFile -> Incorrect file type(" + fileName
					+ "). Only allow import excel file.");
			throw new RuntimeException("BULK_ADJUSTMENT_FILE_TYPE_INCORRECT");
		}

		SimpleDateFormat tsFormat = new SimpleDateFormat("yyyyMMddHHmmss");
		String filePath = this.getOutputPath();
		fullFilePath = filePath + fileNameWithoutExet + tsFormat.format(new Date()) + "." + fileExet;
		File dir = new File(filePath);
		if (!dir.exists() && !dir.isDirectory()) {
			if (!dir.mkdirs()) {
				log.debug("BulkAdjustmentFileHandler.uploadFile -> Directory(" + filePath + ") cannot create.");
				throw new RuntimeException("Directory(" + filePath + ") cannot create.");
			}
		}

		FileOutputStream out = new FileOutputStream(fullFilePath);
		out.write(fileBytes);
		out.flush();
		out.close();

		BulkAdjustment bulkAdjustment = new BulkAdjustment();
		bulkAdjustment.setFileName(fileName);
		bulkAdjustment.setSrcFilePath(fileName);
		bulkAdjustment.setTargetFilePath(fullFilePath);
		return bulkAdjustment;
	}
	

	
	private LocalDate covertDateNum2LocalDate(String dateNum) {
		LocalDate localDate = null;
		if (StringUtils.isBlank(dateNum)) {
			return localDate;
		}
		try {
			if (StringUtils.isNumeric(dateNum)) {
				Date date = HSSFDateUtil.getJavaDate(Double.parseDouble(dateNum));
				Instant instant = date.toInstant();
				ZoneId zone = ZoneId.systemDefault();
				LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
				localDate = localDateTime.toLocalDate();
			} else {
				localDate = LocalDate.parse(dateNum, DateTimeFormatter.ISO_DATE);
			}
		} catch (Exception e) {
		}
		return localDate;
	}

	public List<LinkedHashMap<String, String>> getDataList(List<LinkedHashMap<String, String>> orgDataList, int headerIndex) {
		List<LinkedHashMap<String, String>> dataList = new ArrayList<LinkedHashMap<String, String>>();
		Map<String, String> headerMap = orgDataList.get(headerIndex);
		Iterator<Entry<String, String>> headerIterator = headerMap.entrySet().iterator();
	    while(headerIterator.hasNext()){
	        Entry<String, String> entry = headerIterator.next();
	        headerMap.put(entry.getKey(), this.toCamelCase(entry.getValue()));
	    }
		
		for(int i = 0, size = orgDataList.size(); i < size; i++) {
			if (headerIndex >= i) {
				continue;
			}
			LinkedHashMap<String, String> orgRowDataMap = orgDataList.get(i);
			LinkedHashMap<String, String> rowDataMap = new LinkedHashMap<String, String>(orgRowDataMap.size());
			for (Entry<String, String> entry : orgRowDataMap.entrySet()) {
				if (COLUMN_NAME_ROW_NUM.equals(entry.getKey())) {
					rowDataMap.put(entry.getKey(), entry.getValue());
				}
				String key = headerMap.get(entry.getKey());
				rowDataMap.put(key, entry.getValue());
			}
			dataList.add(rowDataMap);
		}
		return dataList;
	}

	public List<LinkedHashMap<String, String>> getBulkAdjUserDataFromExcel(String filePath, String sheetName) {
		if (!filePath.endsWith(EXCEL_EXET_XLSX)) {
			log.debug("BulkAdjustmentFileHandler.getBulkAdjOrderDataFromExcel -> Incorrect file({}). Only allow import excel file.", filePath);
			throw new RuntimeException("BULK_ADJUSTMENT_FILE_TYPE_INCORRECT");
		}

		LinkedHashMap<String, List<LinkedHashMap<String, String>>> allSheetDataMap = null;
		List<LinkedHashMap<String, String>> dataList = null;
		try {
			ExcelHandler reader = new ExcelHandler();
			reader.readAllSheets(filePath);
			allSheetDataMap = reader.getAllSheetDataList();
			dataList = this.getDataList(allSheetDataMap.get(sheetName), 0);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		if (dataList == null || dataList.isEmpty()) {
			log.debug("BulkAdjustmentFileHandler.getBulkAdjDataFromExcel -> dataList is null");
			throw new RuntimeException("BULK_ADJUSTMENT_FILE_IS_EMPTY");
		}

		return dataList;
	}

	private String toCamelCase(String str) {
		String regex = "[\n`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。，、？]";
		Pattern p = Pattern.compile(regex);
		Matcher m = p.matcher(str);
		String s = Stream.of(m.replaceAll("").trim().split("\\s+")).map(e -> e.substring(0, 1).toUpperCase() + e.substring(1)).reduce("",
				(a, b) -> a + b);
		return Character.toLowerCase(s.charAt(0)) + s.substring(1);
	}
}